/*
 * Copyright (C) 2005-2015 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package org.springframework.extensions.directives;

import java.io.IOException;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.extensibility.ContentModelElement;
import org.springframework.extensions.surf.extensibility.ExtensibilityDirectiveData;
import org.springframework.extensions.surf.extensibility.ExtensibilityModel;
import org.springframework.extensions.surf.extensibility.impl.DefaultContentModelElement;
import org.springframework.extensions.surf.extensibility.impl.DiscardUnboundContentModelElementImpl;

import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;

/**
 * <p>Allows the JavaScript content generated by the {@link OutputJavaScriptDirective} to be relocated within the context of a model. 
 * The reason this directive is required is that the {@link OutputJavaScriptDirective} needs to be initially placed in the model before
 * any directives that use its {@link DefaultContentModelElement} approach (such as the AggregateJavaScriptDependencyDirective and
 * the {@link CreateWebScriptWidgetsDirective}) can add content for it to render. However, the proper location for JavaScript is at the
 * end of an HTML page so this directive can be used to move the generated JavaScript to another location on the page before the 
 * model is flushed.</p>
 *  
 * @author David Draper
 */
public class RelocateJavaScriptOutputDirective extends AbstractDependencyExtensibilityDirective
{
    private static final Log logger = LogFactory.getLog(RelocateJavaScriptOutputDirective.class);
    
    public RelocateJavaScriptOutputDirective(String directiveName, ExtensibilityModel model)
    {
        super(directiveName, model);
    }

    @SuppressWarnings("rawtypes")
    @Override
    public void execute(Environment env,
                        Map params,
                        TemplateModel[] loopVars,
                        TemplateDirectiveBody body) throws TemplateException, IOException
    {
        super.execute(env, params, loopVars, body);
        getModel().clearRelocatedContent(OutputJavaScriptDirective.OUTPUT_DEPENDENCY_DIRECTIVE_ID, OutputJavaScriptDirective.OUTPUT_JS_DEPENDENCIES_DIRECTIVE_NAME);
    }
    
    /**
     * <p>Attempts to retrieve a {@link OutputJavaScriptContentModelElement} that has been added to the {@link ExtensibilityModel} by through
     * the use of a {@link OutputJavaScriptDirective} directive. However, if this {@link ContentModelElement} cannot be found then it will be
     * substituted by an {@link DiscardUnboundContentModelElementImpl} which whilst not generating any error in processing may cause unexpected
     * behaviour in the HTML page because no JavaScript relocation will take place.</p>
     */
    @SuppressWarnings("rawtypes")
    @Override
    public ExtensibilityDirectiveData createExtensibilityDirectiveData(String id, 
                                                                       String action,
                                                                       String target,
                                                                       Map params,
                                                                       TemplateDirectiveBody body, 
                                                                       Environment env)
    {
        // Attempt to retrieve the content element provided by the OutputJavaScript directive. If this content element cannot be 
        // found then it is likely that the relocate JavaScript directive has been used before it. If this is the case then we will
        // simply generate a warning message and use a content model element that will be discarded when flushed. Essentially the
        // result will be that no relocation occurs.
        ContentModelElement content = getModel().findContentModelElement(OutputJavaScriptDirective.OUTPUT_DEPENDENCY_DIRECTIVE_ID);
        if (content == null)
        {
            if (logger.isWarnEnabled())
            {
                logger.warn("An attempt has been made to reloate a " + OutputJavaScriptDirective.OUTPUT_JS_DEPENDENCIES_DIRECTIVE_NAME +
                            " before it has been processed. No relocation will occur");
            }
            content = new DiscardUnboundContentModelElementImpl();
        }
        return new RelocateJavaScriptOutputDirectiveData(id, action, target, body, env, (OutputJavaScriptContentModelElement) content);
    }
}
